﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;

using Styx.WoWInternals.DBC;
using Styx.WoWInternals.WoWObjects;
using Styx.WoWInternals;
using Styx.Common;
using Styx.CommonBot.Routines;
using Styx.Helpers;
using Styx.Pathing;
using Styx;
using System.Diagnostics;
using System.Threading;
using System.IO;
using Styx.CommonBot;
using System.Windows.Forms;
using Styx.CommonBot.Inventory;

namespace KingWoW
{
    class ArcaneMageCombatClass : KingWoWAbstractBaseClass
    {

        private static string Name = "KingWoW ArcaneMage'";

        #region CONSTANT AND VARIABLES

        //START OF CONSTANTS ==============================
        private string[] StealBuffs = { "Innervate", "Hand of Freedom", "Hand of Protection", "Regrowth", "Rejuvenation", "Lifebloom", "Renew", 
                                      "Hand of Salvation", "Power Infusion", "Power Word: Shield", "Arcane Power", "Hot Streak!", /*"Avenging Wrath",*/ 
                                      "Elemental Mastery", "Nature's Swiftness", "Divine Plea", "Divine Favor", "Icy Veins", "Ice Barrier", "Holy Shield", 
                                      "Divine Aegis", "Bloodlust", "Time Warp", "Brain Freeze"};
        private static bool IsCRPaused = false;
        private const bool LOGGING = true;
        private const bool DEBUG = false;
        private const bool TRACE = false;
        private static KingWoWUtility utils = null;
        private Movement movement = null;
        private ExtraUtils extra = null;

        private int arcane_explosion_radius = 10;
        WoWUnit target = null;
        private WoWUnit tank = null;
        private WoWUnit off_tank = null;
        private WoWUnit lastTank = null;
        private WoWUnit lastOffTank = null;
        private bool SoloBotType = false;
        private string BaseBot = "unknown";
        private DateTime nextTimeRuneOfPowerAllowed;
        private TalentManager talents = null;
        private WoWUnit SupernovaCandidateTarget = null;
        private const string DEBUG_LABEL = "DEBUG";
        private const string TRACE_LABEL = "TRACE";
        private const string TANK_CHANGE = "TANK CHANGED";
        private const string OFF_TANK_CHANGE = "OFF_TANK CHANGED";
        private const string FACING = "FACING";

        private Random rnd = new Random();
        private int RANDOM_TIME = 0;

        //START OF SPELLS AND AURAS ==============================
        private const string DRINK = "Drink";
        private const string FOOD = "Food";

        private const string FROSTFIRE_BOLT = "Frostfire Bolt";
        private const string FROST_NOVA = "Frost Nova";
        private const string BLINK = "Blink";
        private const string COUNTERSPELL = "Counterspell";
        private const string SUMMON_WATER_ELEMENTAL = "Summon Water Elemental";
        private const string FROSTBOLT = "Frostbolt";
        private const string POLYMORPH = "Polymorph";
        private const string ARCANE_EXPLOSION = "Arcane Explosion";
        private const string FINGER_OF_FROST = "Fingers of Frost";
        private const string ICE_BLOCK = "Ice Block";
        private const string CONE_OF_COLD = "Cone of Cold";
        private const string REMOVE_CURSE = "Remove Curse";
        private const string SLOW_FALL = "Slow Fall";
        private const string MOLTEN_ARMOR = "Molten Armor";
        private const string ICY_VEINS = "Icy Veins";
        private const string CONJURE_REFRESHMENT = "Conjure Refreshment";
        private const string MIRROR_IMAGE = "Mirror Image";
        private const string BLIZZARD = "Blizzard";
        private const string INVISIBILITY = "Invisibility";
        private const string ARCANE_BRILLANCE = "Arcane Brilliance";
        private const string SPELLSTEAL = "Spellsteal";
        private const string CONJURE_REFRESHMENT_TABLE = "Conjure Refreshment Table";
        private const string TIME_WARP = "Time Warp";
        private const string ALTER_TIME = "Alter Time";
        private const int ICE_FLOES_BUFF = 108839;

        private const string ARCANE_BARRAGE = "Arcane Barrage";
        private const string ARCANE_MISSILES = "Arcane Missiles";
        private const string ARCANE_MISSILES_PROC = "Arcane Missiles!";
        private const string ARCANE_BLAST = "Arcane Blast";
        private const string ARCANE_CHARGE = "Arcane Charge";
        private const string ARCANE_POWER = "Arcane Power";
        private const string ARCANE_ORB = "Arcane Orb";
        private const string EVOCATION = "Evocation";        

        private const string PROFOUND_MAGIC_T16_2P_BONUS = "Profound Magic";
        private const string ARCANE_INSTABILITY_T17_4P_BONUS = "Arcane Instability";
        
        //END OF SPELLS AND AURAS ==============================

        //TALENTS
        private const string PRESENCE_OF_MIND = "Presence of Mind";
        private const string ICE_FLOES = "Ice Floes";
        private const string BLAZING_SPEED = "Blazing Speed";
        private const string ICE_BARRIER = "Ice Barrier";
        private const string RING_OF_FROST = "Ring of Frost";
        private const string ICE_WARD = "Ice Ward";
        private const string FROSTJAW = "Frostjaw";
        private const string GREATER_INVISIBILITY = "Greater Invisibility";
        private const string SUPERNOVA = "Supernova";
        private const string COLD_SNAP = "Cold Snap";
        private const string NETHER_TEMPEST = "Nether Tempest";
        private const string EVANESCE = "Evanesce";
        private const string RUNE_OF_POWER = "Rune of Power";
        private const string PRISMATIC_CRYSTAL = "Prismatic Crystal";
        //END TALENTS
        //END OF CONSTANTS ==============================

        #endregion

        public double TimeEvocationAgainUp()
        {
            string command = "local start, duration, enabled = GetSpellCooldown(\"Evocation\"); if start > 0 then return (start + duration - GetTime()) else return 0 end";
            double currResolve = Lua.GetReturnVal<double>(command, 0);
            return currResolve; 		
        }
        

        #region Hotkeys

        private static void InitializeHotkey()
        {
            HotkeysManager.Initialize(StyxWoW.Memory.Process.MainWindowHandle);
        }

        private static DateTime nextTime_Pause_Resume_Allowed = DateTime.Now;

        public static void SetnextTime_Pause_Resume_Allowed()
        {
            //1 seconds avoid duuble cast
            nextTime_Pause_Resume_Allowed = DateTime.Now + new TimeSpan(0, 0, 0, 0, 1000);
        }
        
        private static Hotkey RegisterHotkeyAssignment(string name, Keys key, Action<Hotkey> callback)
        {
            Keys keyCode = key & Keys.KeyCode;
            ModifierKeys mods = ModifierKeys.NoRepeat;

            if ((key & Keys.Shift) != 0)
                mods |= ModifierKeys.Shift;
            if ((key & Keys.Alt) != 0)
                mods |= ModifierKeys.Alt;
            if ((key & Keys.Control) != 0)
                mods |= ModifierKeys.Control;

            return HotkeysManager.Register(name, keyCode, mods, callback);
        }

        private static void RegisterHotkeys()
        {
            RegisterHotkeyAssignment("Routine Pause", (Keys)ArcaneMageSettings.Instance.PauseKey, hk =>
            {
                if (nextTime_Pause_Resume_Allowed < DateTime.Now)
                {
                    if (!IsCRPaused)
                    {
                        IsCRPaused = true;
                        if (ExtraUtilsSettings.Instance.ViewChatMessages)
                            Lua.DoString(@"print('\124cFFE61515 Paused!')");
                        Logging.Write("KingWoW Routine Paused!");
                    }
                    else
                    {
                        IsCRPaused = false;
                        if (ExtraUtilsSettings.Instance.ViewChatMessages)
                            Lua.DoString(@"print('\124cFF15E61C Resumed!')");
                        Logging.Write("KingWoW Routine Resumed!");
                    }
                    SetnextTime_Pause_Resume_Allowed();
                }
            });
            RegisterHotkeyAssignment("AvoidAOE", (Keys)ArcaneMageSettings.Instance.AvoidAOEKey, hk =>
            {
                ArcaneMageSettings.Instance.AvoidAOE = !ArcaneMageSettings.Instance.AvoidAOE;
                if (ArcaneMageSettings.Instance.AvoidAOE)
                {
                    if (ExtraUtilsSettings.Instance.ViewChatMessages) 
                        Lua.DoString(@"print('AvoidAOE \124cFF15E61C Enabled!')");
                    Logging.Write("AvoidAOE Enabled!");
                }

                else
                {
                    if (ExtraUtilsSettings.Instance.ViewChatMessages) 
                        Lua.DoString(@"print('AvoidAOE \124cFFE61515 Disabled!')");
                    Logging.Write("AvoidAOE Disabled!");
                }

            });

            RegisterHotkeyAssignment("BurstKey", (Keys)ArcaneMageSettings.Instance.BurstKey, hk =>
            {              
                if (ExtraUtilsSettings.Instance.ViewChatMessages)
                    Lua.DoString(@"print('BURST!!!')");
                Logging.Write("BurstKeyPressed!");

                BurstModeActivated = true;
                BurstTimer.AutoReset = false;
                BurstTimer.Interval = 10000;
                BurstTimer.Start();
            });
        }

        private static bool BurstModeActivated = false;
        private static System.Timers.Timer BurstTimer = new System.Timers.Timer();

        private bool BurstHotkeyHandle()
        {
            if (BurstModeActivated)
            {
                if (utils.CanCast(ARCANE_POWER) && ArcaneMageSettings.Instance.CDUseArcanePower == ArcaneMageSettings.CDUseType.OnBURSTHOTKEY)
                {
                    utils.LogActivity("BURST HOTKEY TRY CAST:" + ARCANE_POWER);
                    utils.Cast(ARCANE_POWER);
                }
                if (utils.CanCast(MIRROR_IMAGE) && ArcaneMageSettings.Instance.CDUseMirrorImage == ArcaneMageSettings.CDUseType.OnBURSTHOTKEY)
                {
                    utils.LogActivity("BURST HOTKEY TRY CAST:" + MIRROR_IMAGE);
                    utils.Cast(MIRROR_IMAGE);
                }
                if (utils.CanCast(PRISMATIC_CRYSTAL) && ArcaneMageSettings.Instance.PrismaticCrystalOnlyOnBurst)
                {
                    if (Me.CurrentTarget != null && Me.CurrentTarget.Attackable && Me.CurrentTarget.IsValid)
                    {
                        utils.LogActivity("BURST HOTKEY TRY CAST:" + PRISMATIC_CRYSTAL, Me.CurrentTarget.SafeName);
                        utils.Cast(PRISMATIC_CRYSTAL, Me.CurrentTarget);
                        SpellManager.ClickRemoteLocation(Me.CurrentTarget.Location);
                    }
                }
            }
            return false;
        }

        private void RemoveHotkeys()
        {
            HotkeysManager.Unregister("Routine Pause");
            HotkeysManager.Unregister("AvoidAOE");
            HotkeysManager.Unregister("BurstKey");
        }

        private void ReRegisterHotkeys()
        {
            RemoveHotkeys();
            RegisterHotkeys();
        }

        #endregion
        
        public ArcaneMageCombatClass()
        {
            utils = new KingWoWUtility();
            movement = new Movement();
            extra = new ExtraUtils();
            tank = null;
            lastTank = null; ;
            SoloBotType = false;
            BaseBot = "unknown";
            nextTimeRuneOfPowerAllowed = DateTime.Now;
            nextTimeTryReviveAllowed = DateTime.Now;
            talents = new TalentManager();
            BurstTimer.Elapsed += BurstTimer_Elapsed;
        }

        void BurstTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
        {
            if (ExtraUtilsSettings.Instance.ViewChatMessages)
                Lua.DoString(@"print('BURST ENDS!!!')");
            Logging.Write("BurstModeEnd!");
            BurstModeActivated = false;
        }

        private DateTime nextTimeTryReviveAllowed;

        public void SetNextTimeTryRevive()
        {
            //2 seconds avoid duuble cast
            nextTimeTryReviveAllowed = DateTime.Now + new TimeSpan(0, 0, 0, 0, 3000);
        }

        private bool MyDeath()
        {
            if (SoloBotType && Me.IsDead && !Me.IsGhost && nextTimeTryReviveAllowed < DateTime.Now)
            {
                Lua.DoString(string.Format("RunMacroText(\"{0}\")", "/script RepopMe()"));
                SetNextTimeTryRevive();
            }
            return false;
        }

        private void SetTargetToAttack()
        {
            if (ArcaneMageSettings.Instance.TargetTypeSelected == ArcaneMageSettings.TargetType.AUTO)
            {
                if (ExtraUtilsSettings.Instance.provinGround_Targeting)
                    target = utils.getAnyTargetToAttack(40f);
                else
                    target = utils.getTargetToAttack(40, tank, false, ExtraUtilsSettings.Instance.UseBossLogic);
                if (target != null)
                    target.Target();
            }
            else
                target = Me.CurrentTarget;
        }

        public override bool Combat
        {
            get
            {
                updateValue();
                if (Me.IsCasting && missiles==3 && Me.CastingSpell.Name.Equals(ARCANE_BLAST))
                    SpellManager.StopCasting();
                if (Me.IsChanneling && Me.ChanneledSpell.Name.Equals(EVOCATION))
                {
                    goForEvocation = false;
                    if(Me.ManaPercent >= 95)
                    SpellManager.StopCasting();
                }
                if ((Me.Mounted && !ArcaneMageSettings.Instance.AutoDismountOnCombat)
                    || !StyxWoW.IsInGame || !StyxWoW.IsInWorld || Me.Silenced
                    || (!ExtraUtilsSettings.Instance.FastRotation && (utils.IsGlobalCooldown() || Me.IsCasting))
                    || utils.isAuraActive(DRINK) || utils.isAuraActive(FOOD) || IsCRPaused || Me.IsChanneling)
                    return false;

                tank = utils.SimpleGetTank(40f);
                if (tank == null || !tank.IsValid || !tank.IsAlive) tank = Me;

                if (tank != null && (lastTank == null || lastTank.Guid != tank.Guid))
                {
                    lastTank = tank;
                    utils.LogActivity(TANK_CHANGE, tank.Class.ToString());
                } off_tank = utils.SimpleGetOffTank(40f, tank);
                if (off_tank != null && (lastOffTank == null || lastOffTank.Guid != off_tank.Guid))
                {
                    lastOffTank = off_tank;
                    utils.LogActivity(OFF_TANK_CHANGE, off_tank.Class.ToString());
                }
                return CombatRotation();
            }
        }

        public override bool Pulse
        {
            get
            {
                if (Me.IsDead) return MyDeath();
                if (Me.Combat || (Me.Mounted && !ArcaneMageSettings.Instance.AutoDismountOnCombat)
                    || !StyxWoW.IsInGame || !StyxWoW.IsInWorld || Me.Silenced || utils.IsGlobalCooldown()
                    || utils.MeIsChanneling || Me.IsCasting
                    || utils.isAuraActive(DRINK) || utils.isAuraActive(FOOD) || IsCRPaused)
                    return false;
                
                tank = utils.SimpleGetTank(40f);
                if (tank == null || !tank.IsValid || !tank.IsAlive) tank = Me;

                if (tank != null && (lastTank == null || lastTank.Guid != tank.Guid))
                {
                    lastTank = tank;
                    utils.LogActivity(TANK_CHANGE, tank.SafeName);
                }
                off_tank = utils.SimpleGetOffTank(40f, tank);
                if (off_tank != null && (lastOffTank == null || lastOffTank.Guid != off_tank.Guid))
                {
                    lastOffTank = off_tank;
                    utils.LogActivity(OFF_TANK_CHANGE, off_tank.SafeName);
                }
                if (!Me.Combat && ((tank != null && tank.Combat) || (off_tank != null && off_tank.Combat)))
                    return CombatRotation();
                return false;
            }
        }

        public override bool Pull
        {
            get
            {
                target = Me.CurrentTarget;
                if (target != null && target.IsDead)
                {
                    Me.ClearTarget();
                    target = utils.getTargetToAttack(40, tank, false, ExtraUtilsSettings.Instance.UseBossLogic);
                    target.Target();
                }
                if ((SoloBotType || ArcaneMageSettings.Instance.movementEnabled) && Me.CurrentTarget != null && Me.IsMoving && Me.CurrentTarget.InLineOfSpellSight && Me.CurrentTarget.Distance - Me.CurrentTarget.CombatReach - 1 <= ArcaneMageSettings.Instance.PullDistance)
                {
                    Navigator.PlayerMover.MoveStop();
                }
                if (target != null && !target.IsFriendly && target.Attackable && !target.IsDead && !IsCRPaused)
                {
                    if (!target.InLineOfSpellSight || target.Distance - target.CombatReach -1 > ArcaneMageSettings.Instance.PullDistance)
                    {
                        movement.KingHealMove(target, ArcaneMageSettings.Instance.PullDistance);
                    }
                    if (utils.CanCast(ARCANE_BLAST) && !Me.IsMoving && ((utils.PlayerCountBuff(ARCANE_CHARGE) < 4 || utils.isAuraActive(ARCANE_POWER))))
                    {
                        if (!Me.IsMoving && !Me.IsFacing(target))
                        {
                            utils.LogActivity(FACING, target.SafeName);
                            Me.SetFacing(target);
                        }

                        utils.LogActivity(ARCANE_BLAST, target.SafeName);
                        return utils.Cast(ARCANE_BLAST, target);
                    }
                }
                return false;
            }
        }

        public override bool Initialize
        {
            get
            {
                Logging.Write("Ciao " + Me.Class.ToString());
                Logging.Write("Welcome to " + Name + " custom class");
                Logging.Write("Tanks All HonorBuddy Forum developers for code inspiration!");
                Logging.Write("Powered by Attilio76");
                BotEvents.OnBotStartRequested += new BotEvents.OnBotStartStopRequestedDelegate(BotEvents_OnBotStart);
                InitializeHotkey();
                RegisterHotkeys();
                Logging.Write("************ XML CONFIGURATION ************");
                Logging.Write(ArcaneMageSettings.Instance.GetXML().ToString());
                Logging.Write("*******************************************");
                Logging.Write("*******************************************");
                Logging.Write("*******************************************");
                Logging.Write("*******************************************");
                Logging.Write("************ EXTRA CONFIGURATION ************");
                Logging.Write(ExtraUtilsSettings.Instance.GetXML().ToString());
                Logging.Write("*******************************************");
                return true;
            }
        }

        void BotEvents_OnBotStart(EventArgs args)
        {
            ReRegisterHotkeys();
            talents.Update();
            BotUpdate();
            if (talents.HasGlyph("Arcane Explosion"))
            {
                Logging.Write("Glyph of Arcane Explosion Detected: set Arcane Explosion Radius to 15y");
                arcane_explosion_radius = 15;
            }

        }

        public override bool UpdateTalentAndGlyph
        {
            get
            {
                ReRegisterHotkeys();
                talents.Update();
                BotUpdate();
                if (talents.HasGlyph("Arcane Explosion"))
                {
                    Logging.Write("Glyph of Arcane Explosion Detected: set Arcane Explosion Radius to 15y");
                    arcane_explosion_radius = 15;
                }
                return true;

            }
        }

        public override bool NeedCombatBuffs { get { return Buff(); } }

        public override bool NeedPreCombatBuffs { get { return Buff(); } }

        public override bool NeedPullBuffs { get { return Buff(); } }

        private bool Buff()
        {
            if (Me.Mounted || IsCRPaused || Me.InVehicle
                    || !StyxWoW.IsInGame || !StyxWoW.IsInWorld || Me.Silenced
                    || utils.isAuraActive(DRINK) || utils.isAuraActive(FOOD) || Me.IsChanneling || IsCRPaused 
                /*|| utils.MeIsCastingWithLag()*/)
                return false;

            
            //arcane brillance
            if (ArcaneMageSettings.Instance.AutoBuffBrillance && !utils.isAuraActive(ARCANE_BRILLANCE) && utils.CanCast(ARCANE_BRILLANCE))
            {
                utils.LogActivity(ARCANE_BRILLANCE);
                return utils.Cast(ARCANE_BRILLANCE);
            }
            return false;
        }

        public override bool NeedRest
        {
            get
            {
                if (Me.IsDead || Me.Mounted || Me.InVehicle || (Me.HealthPercent >= 100 && Me.ManaPercent >= 100)) return false;
                if (utils.IsEatingOrDrinking() )
                    return true;
                if (!Me.Combat && !Me.IsCasting && !utils.IsGlobalCooldown() && !utils.GotMagefood && utils.CanCast(CONJURE_REFRESHMENT))
                {
                    utils.LogActivity(CONJURE_REFRESHMENT);
                    return utils.Cast(CONJURE_REFRESHMENT);
                }
                if (Me.ManaPercent <= ArcaneMageSettings.Instance.ManaPercent
                    && !Me.Combat && !Me.IsMoving && !utils.IsEatingOrDrinking())
                {
                    WoWItem mydrink = Consumable.GetBestDrink(false);
                    if (mydrink != null)
                    {
                        utils.LogActivity("Drinking");
                        Styx.CommonBot.Rest.DrinkImmediate();
                        return true;
                    }
                    return true;
                }
                if (Me.HealthPercent <= ArcaneMageSettings.Instance.HealthPercent
                    && !Me.Combat && !Me.IsMoving && !utils.IsEatingOrDrinking())
                {
                    WoWItem myfood = Consumable.GetBestFood(false);
                    if (myfood != null)
                    {
                        utils.LogActivity("Eating");
                        Styx.CommonBot.Rest.DrinkImmediate();
                        return true;
                    }
                    return true;
                }
                return false;
            }
        }

        private bool BotUpdate()
        {
            if (BaseBot.Equals(BotManager.Current.Name))
                return false;
            if (utils.IsBotBaseInUse("LazyRaider") || utils.IsBotBaseInUse("Tyrael")
                || utils.IsBotBaseInUse("Raid Bot") || utils.IsBotBaseInUse("Enyo"))
            {
                SoloBotType = false;
                BaseBot = BotManager.Current.Name;
                Logging.Write("Base bot detected: " + BotManager.Current.Name);
                return true;
            }

            Logging.Write("Base bot detected: " + BotManager.Current.Name);
            SoloBotType = true;
            BaseBot = BotManager.Current.Name;
            return true;


        }

        public void SetNextTimeRuneOfPower()
        {
            //3 seconds wait to avoid popping 2 rune of frost cause has high priority
            nextTimeRuneOfPowerAllowed = DateTime.Now + new TimeSpan(0, 0, 0, 0, 3000);
        }

        private DateTime nextTimeIceFloesAllowed=DateTime.Now;

        public void SetNextTimeIceFloesAllowed()
        {
            //1 seconds wait to avoid popping 2 rune of frost cause has high priority
            nextTimeIceFloesAllowed = DateTime.Now + new TimeSpan(0, 0, 0, 0, 1000);
        }


        private bool PriorityBuff()
        {
            
            if (ArcaneMageSettings.Instance.UseRuneOfPower && !Me.IsMoving && nextTimeRuneOfPowerAllowed < DateTime.Now && !utils.isAuraActive(RUNE_OF_POWER, Me) && Me.Combat && utils.CanCast(RUNE_OF_POWER))
            {
                utils.LogActivity(RUNE_OF_POWER);
                utils.Cast(RUNE_OF_POWER);
                SetNextTimeRuneOfPower();
                return SpellManager.ClickRemoteLocation(Me.Location);
            }

            if (Me.Combat && ArcaneMageSettings.Instance.UseIcebarrier && utils.CanCast(ICE_BARRIER) && Me.HealthPercent <= ArcaneMageSettings.Instance.IcebarrierHP && !utils.isAuraActive(ICE_BARRIER))
            {
                utils.LogActivity(ICE_BARRIER);
                return utils.Cast(ICE_BARRIER);
            }

            if (ArcaneMageSettings.Instance.IceWardOnTank && Me.Combat && (tank != null && tank.Combat) && utils.CanCast(ICE_WARD) && 
                !utils.isAuraActive(ICE_WARD, tank) && tank != null && tank.IsAlive && tank.Distance < 40 &&
                tank.InLineOfSpellSight)
            {
                utils.LogActivity(ICE_WARD, tank.Class.ToString());
                return utils.Cast(ICE_WARD, tank);
            }
            return false;
        }

        private bool Interrupt()
        {
            if (ArcaneMageSettings.Instance.AutoInterrupt)
            {
                WoWUnit InterruptTargetCandidate = Me.FocusedUnit;
                if (InterruptTargetCandidate == null || InterruptTargetCandidate.IsFriendly || InterruptTargetCandidate.IsDead
                    || !InterruptTargetCandidate.Attackable || !InterruptTargetCandidate.InLineOfSpellSight
                    || InterruptTargetCandidate.Distance - InterruptTargetCandidate.CombatReach - 1 > 40f)
                {
                    if (Me.CurrentTarget != null && Me.CurrentTarget.Distance - Me.CurrentTarget.CombatReach - 1 <= 40f && Me.CurrentTarget.InLineOfSpellSight)
                        InterruptTargetCandidate = Me.CurrentTarget;
                    else if (target != null && target.Distance - target.CombatReach - 1 <= 40f && target.InLineOfSpellSight)
                        InterruptTargetCandidate = target;
                }
                if (InterruptTargetCandidate != null && (InterruptTargetCandidate.IsCasting || InterruptTargetCandidate.IsChanneling)
                    && InterruptTargetCandidate.CanInterruptCurrentSpellCast && utils.CanCast(COUNTERSPELL, InterruptTargetCandidate)
                    && Me.IsFacing(InterruptTargetCandidate)
                    && InterruptTargetCandidate.Distance - InterruptTargetCandidate.CombatReach - 1 <= 40f
                    && InterruptTargetCandidate.InLineOfSpellSight)
                {
                    RANDOM_TIME = rnd.Next(ArcaneMageSettings.Instance.Interrupt_MIN_Time, ArcaneMageSettings.Instance.Interrupt_MAX_Time);

                    if (InterruptTargetCandidate.CurrentCastStartTime.AddMilliseconds(RANDOM_TIME) <= DateTime.Now)
                    {
                        utils.LogActivity(COUNTERSPELL, InterruptTargetCandidate.SafeName);
                        return utils.Cast(COUNTERSPELL, InterruptTargetCandidate);
                    }
                }
            }
            return false;
        }

        private bool Defensivececk()
        {
            if (Me.Combat && ArcaneMageSettings.Instance.EvanesceUse && utils.CanCast(EVANESCE) && Me.HealthPercent < ArcaneMageSettings.Instance.EvanesceHP && !StyxWoW.Me.ActiveAuras.ContainsKey("Hypothermia"))
            {
                utils.LogActivity(EVANESCE, Me.Class.ToString());
                return utils.Cast(EVANESCE);
            }

            if (Me.Combat && ArcaneMageSettings.Instance.IceBlockUse && utils.CanCast(ICE_BLOCK) && Me.HealthPercent < ArcaneMageSettings.Instance.IceBlockHP && !StyxWoW.Me.ActiveAuras.ContainsKey("Hypothermia"))
            {
                utils.LogActivity(ICE_BLOCK, Me.Class.ToString());
                return utils.Cast(ICE_BLOCK);
            }

            if (Me.Combat && ArcaneMageSettings.Instance.ColdSnapUse && utils.CanCast(COLD_SNAP) && Me.HealthPercent < ArcaneMageSettings.Instance.ColdSnapHP)
            {
                utils.LogActivity(COLD_SNAP, Me.Class.ToString());
                return utils.Cast(COLD_SNAP);
            }

            if (Me.Combat && ArcaneMageSettings.Instance.UseBlink && utils.CanCast(BLINK) && Me.IsMoving && (utils.AllAttaccableEnemyMobsInRange(12).Count() >= 1))
            {
                utils.LogActivity(BLINK);
                return utils.Cast(BLINK);
            }
            return false;

        }

        private bool SpellSteal()
        {
            if (ArcaneMageSettings.Instance.UseSpellSteal)
            {
                WoWUnit CandidateUnit = null;
                if (Me.FocusedUnit != null && Me.FocusedUnit.Attackable && Me.FocusedUnit.Distance - Me.FocusedUnit.CombatReach -1  <= 30 && Me.FocusedUnit.InLineOfSpellSight)
                    CandidateUnit = Me.FocusedUnit;
                else if (Me.CurrentTarget != null && Me.CurrentTarget.Attackable && Me.CurrentTarget.Distance - Me.CurrentTarget.CombatReach -1  <= 30 && Me.CurrentTarget.InLineOfSpellSight)
                    CandidateUnit = Me.CurrentTarget;
                if (CandidateUnit != null && utils.CanCast(SPELLSTEAL, CandidateUnit, true))
                    return SpellStealOnUnit(CandidateUnit);
            }

            return false;
        }

        private bool SpellStealOnUnit(WoWUnit u)
        {           
           foreach (string spell in StealBuffs)
            {
                if (u.HasAura(spell) && !StyxWoW.Me.HasAura(spell) && StyxWoW.Me.ManaPercent > 40)
                {
                    utils.LogActivity(SPELLSTEAL + spell, u.SafeName);
                    return utils.Cast(SPELLSTEAL, u);
                }
            }           
            return false;
        }

        private bool IsViableForPolymorph(WoWUnit unit)
        {
            if (utils.IsCrowdControlledPlayer(unit))
                return false;

            if (unit.CreatureType != WoWCreatureType.Beast && unit.CreatureType != WoWCreatureType.Humanoid)
                return false;

            if (StyxWoW.Me.CurrentTarget != null && StyxWoW.Me.CurrentTarget == unit)
                return false;

            /*if (StyxWoW.Me.GroupInfo.IsInParty && StyxWoW.Me.PartyMembers.Any(p => p.CurrentTarget != null && p.CurrentTarget == unit))
                 return false;*/

            return true;
        }

        private bool Polymorf()
        {
            if (ArcaneMageSettings.Instance.UsePolymorf)
            {
                if (Me.FocusedUnit != null && Me.FocusedUnit.Attackable && Me.FocusedUnit.Distance - Me.FocusedUnit.CombatReach -1  <= 30
                    && Me.FocusedUnit.InLineOfSpellSight && IsViableForPolymorph(Me.FocusedUnit) && utils.CanCast(POLYMORPH, Me.FocusedUnit, true)
                    && target.Guid != Me.FocusedUnit.Guid)
                {
                    utils.LogActivity(POLYMORPH, Me.FocusedUnit.SafeName);
                    return utils.Cast(POLYMORPH, Me.FocusedUnit);
                }

            }
            return false;

        }

        private uint missiles = 0;
        private uint charges = 0;

        private void updateValue()
        {
            missiles = utils.PlayerCountBuff(ARCANE_MISSILES_PROC);
            charges = utils.PlayerCountBuff(ARCANE_CHARGE);
        }

        private bool UseTalents()
        {
            //apply  Nether Tempest IF TALENTED and always refresh it right before the last tick;
            if (utils.CanCast(NETHER_TEMPEST) && charges >= 4 && (utils.MyAuraTimeLeft(NETHER_TEMPEST, target) <= 3600)
                && !(target.IsPlayer && ArcaneMageSettings.Instance.AvoidDOTPlayers))
            {
                utils.LogActivity(NETHER_TEMPEST, target.SafeName);
                return utils.Cast(NETHER_TEMPEST, target);
            }

            //Supernova at better target with mobs around (if Incanter's Flow talented: cast at 5 stack)
            if (utils.CanCast(SUPERNOVA) && (!talents.IsSelected(18) || (talents.IsSelected(18) && utils.PlayerCountBuff("Incanter's Flow") == 5) || ArcaneMageSettings.Instance.supernovaOnCooldown)
                && !ArcaneMageSettings.Instance.AvoidAOE)
            {

                SupernovaCandidateTarget = utils.EnemyInRangeWithMobsAround(40, 10, ArcaneMageSettings.Instance.SupernovaCount, false, ExtraUtilsSettings.Instance.provinGround_Targeting);
                if (SupernovaCandidateTarget == null && ArcaneMageSettings.Instance.SupernovaOnSingle)
                    SupernovaCandidateTarget = target;
                if (SupernovaCandidateTarget != null)
                {
                    if (!Me.IsFacing(SupernovaCandidateTarget) && !Me.IsMoving)
                        Me.SetFacing(SupernovaCandidateTarget);
                    if (Me.IsFacing(SupernovaCandidateTarget))
                    {
                        utils.LogActivity(SUPERNOVA, SupernovaCandidateTarget.SafeName);
                        return utils.Cast(SUPERNOVA, SupernovaCandidateTarget);
                    }
                }

            }
            return false;
        }

        private bool AOE()
        {
            //***************************   AOE   START    ***************************//
            if (!ArcaneMageSettings.Instance.AvoidAOE)
            {
                if (ArcaneMageSettings.Instance.UseConeOfCold && utils.CanCast(CONE_OF_COLD)
                    && utils.FacingEnemy(12, ExtraUtilsSettings.Instance.provinGround_Targeting).Count() >= ArcaneMageSettings.Instance.ConeOfColdCount)
                {
                    utils.LogActivity("AOE:   " + CONE_OF_COLD);
                    return utils.Cast(CONE_OF_COLD);
                }
                if (!Me.IsCasting && !utils.IsGlobalCooldown() && charges < 4 && missiles < 3 && utils.CanCast(ARCANE_EXPLOSION)
                    && utils.AllAttaccableEnemyMobsInRange(arcane_explosion_radius).Count() >= ArcaneMageSettings.Instance.AOECount)
                {
                    utils.LogActivity("AOE:   " + ARCANE_EXPLOSION);
                    return utils.Cast(ARCANE_EXPLOSION);
                }
                if (utils.CanCast(ARCANE_BARRAGE) && charges == 4
                    && utils.AllAttaccableEnemyMobsInRangeFromTarget(target, 10, ExtraUtilsSettings.Instance.provinGround_Targeting).Count() >= ArcaneMageSettings.Instance.AOECount)
                {
                    if (utils.CanCast(NETHER_TEMPEST) && charges >= 4 && (utils.MyAuraTimeLeft(NETHER_TEMPEST, target) <= 3600)
                            && !(target.IsPlayer && ArcaneMageSettings.Instance.AvoidDOTPlayers))
                    {
                        utils.LogActivity(NETHER_TEMPEST, target.SafeName);
                        return utils.Cast(NETHER_TEMPEST, target);
                    }
                    utils.LogActivity("AOE:   " + ARCANE_BARRAGE, target.SafeName);
                    return utils.Cast(ARCANE_BARRAGE, target);
                }
            }
            //***************************   AOE   END      ***************************//
            return false;
        }

        private bool ProcWorks()
        {
            if (charges < 4  && Me.IsSafelyFacing(target, 45) && utils.CanCast(ARCANE_ORB))
            {
                utils.LogActivity(ARCANE_ORB, target.SafeName);
                return utils.Cast(ARCANE_ORB, target);
            }
            if (PrismaticCrystal()) return true;
            /*if (utils.isAuraActive(ARCANE_INSTABILITY_T17_4P_BONUS) && !Me.IsMoving && utils.CanCast(ARCANE_MISSILES))
            {
                utils.LogActivity("ARCANE_INSTABILITY:  missiles=" + missiles + " charges=" + charges + "   " + ARCANE_MISSILES, target.SafeName);
                return utils.Cast(ARCANE_MISSILES, target);
            }*/
            //Cast Arcane Missiles if you have 4 stacks of Arcane Charge  and 3 charges of Arcane Missiles.
            if (!Me.IsMoving && missiles >=1 && (missiles == 3 || charges == 4) )
            {
                utils.LogActivity("missiles=" + missiles + " charges=" + charges + "   " + ARCANE_MISSILES, target.SafeName);
                return utils.Cast(ARCANE_MISSILES, target);
            }
            /*if (utils.isAuraActive(ARCANE_POWER) && talents.IsSelected(19) && missiles >= 1 && charges >= 3)
            {
                utils.LogActivity("extend duration of ARCANE POWER: " + ARCANE_MISSILES, target.SafeName);
                return utils.Cast(ARCANE_MISSILES, target);
            }*/
            return false;
        }

        private bool CombatRotation()
        {
            updateValue();
            SetTargetToAttack();
            extra.UseHealPotion_Healthstone();
            while (extra.UseRacials()) return true;
            while (extra.UseTrinket1()) return true;
            while (extra.UseTrinket2()) return true;
            extra.UseManaItem();
            while (Defensivececk()) return true;
            while (Interrupt()) return true;
            while (PriorityBuff()) return true;
            updateValue();
            while (RecMana()) return true;
            updateValue();
            while (UseCD()) return true;
            while (SpellSteal()) return true;
            while (Polymorf()) return true;
            while (Decursing()) return true;

            if ((SoloBotType || ArcaneMageSettings.Instance.movementEnabled) && Me.CurrentTarget != null && Me.IsMoving && Me.CurrentTarget.InLineOfSpellSight && Me.CurrentTarget.Distance - Me.CurrentTarget.CombatReach - 1 <= ArcaneMageSettings.Instance.PullDistance)
            {
                Navigator.PlayerMover.MoveStop();
            }

            if (target != null)
            {
                if ((ArcaneMageSettings.Instance.AutofaceTarget || SoloBotType) && !Me.IsMoving)
                {
                    Me.SetFacing(target);
                }
                while (BurstHotkeyHandle()) return true;
                updateValue();
                while (UseTalents()) return true;
                updateValue();
                while (AOE()) return true;
                updateValue();
                while (ProcWorks()) return true;
                updateValue();
                //PRESENCE OF MIND
                if (utils.CanCast(PRESENCE_OF_MIND) && !utils.isAuraActive(PRESENCE_OF_MIND) )
                {
                    utils.LogActivity(PRESENCE_OF_MIND);
                    utils.Cast(PRESENCE_OF_MIND);
                }
                //Cast Arcane Blast if you are above 93% Mana before your start casting (or 91% with the Tier 16 2-piece bonus).
                if ((!Me.IsMoving || utils.isAuraActive(PRESENCE_OF_MIND) || utils.isAuraActive(ICE_FLOES_BUFF))
                    && !Me.IsCasting && !utils.IsGlobalCooldown()
                    && (Me.ManaPercent >= 93 || (Me.ManaPercent >= 91 && utils.PlayerCountBuff(PROFOUND_MAGIC_T16_2P_BONUS) > 0) || (ArcaneMageSettings.Instance.UseEvocationBurnPhase && TimeEvocationAgainUp() <= ArcaneMageSettings.Instance.TimeSecondsEvocationAgainUp && Me.ManaPercent >= ArcaneMageSettings.Instance.EvocationToRecMana))
                    && missiles < 3 && !goForEvocation)
                {
                    utils.LogActivity(ARCANE_BLAST, target.SafeName);
                    return utils.Cast(ARCANE_BLAST, target);
                }
                updateValue();
                if (Me.ManaPercent < 93 && !(ArcaneMageSettings.Instance.UseEvocationBurnPhase && TimeEvocationAgainUp() <= ArcaneMageSettings.Instance.TimeSecondsEvocationAgainUp && Me.ManaPercent >= ArcaneMageSettings.Instance.EvocationToRecMana))
                {
                    //Cast Arcane Missiles at 4 stacks of Arcane Charge 
                    if ((!Me.IsMoving || utils.isAuraActive(PRESENCE_OF_MIND) || utils.isAuraActive(ICE_FLOES_BUFF)) 
                        && missiles > 0 && charges == 4)
                    {
                        utils.LogActivity(ARCANE_MISSILES, target.SafeName);
                        return utils.Cast(ARCANE_MISSILES, target);
                    }
                    updateValue();
                    //Cast Arcane Barrage at 4 stacks of Arcane Charge
                    if (!goForEvocation && !Me.IsCasting && !utils.IsGlobalCooldown() && utils.CanCast(ARCANE_BARRAGE) && missiles == 0 && charges == 4)
                    {
                        if (utils.CanCast(NETHER_TEMPEST) && charges >= 4 && (utils.MyAuraTimeLeft(NETHER_TEMPEST, target) <= 3600)
                            && !(target.IsPlayer && ArcaneMageSettings.Instance.AvoidDOTPlayers))
                        {
                            utils.LogActivity(NETHER_TEMPEST, target.SafeName);
                            return utils.Cast(NETHER_TEMPEST, target);
                        }
                        utils.LogActivity(ARCANE_BARRAGE, target.SafeName);
                        return utils.Cast(ARCANE_BARRAGE, target);
                    }
                    updateValue();
                }
                updateValue();
                //Cast Arcane Blast filler
                if ((!Me.IsMoving || utils.isAuraActive(PRESENCE_OF_MIND) || utils.isAuraActive(ICE_FLOES_BUFF)) && !Me.IsCasting && !utils.IsGlobalCooldown()
                    && charges < 4 /*&& utils.CanCast(ARCANE_BLAST)*/ && missiles < 3 && !goForEvocation)
                {
                    utils.LogActivity(ARCANE_BLAST, target.SafeName);
                    return utils.Cast(ARCANE_BLAST, target);
                }
                updateValue();
                //****************************  MOVING BUFF ****************************

                //PRESENCE OF MIND
                /*if (Me.IsMoving && utils.CanCast(PRESENCE_OF_MIND) && !utils.isAuraActive(PRESENCE_OF_MIND) && utils.PlayerCountBuff(ARCANE_CHARGE) < 4)
                {
                    utils.LogActivity(PRESENCE_OF_MIND);
                    utils.Cast(PRESENCE_OF_MIND);
                }
                updateValue();*/
                //ICE_FLOE
                if (ArcaneMageSettings.Instance.autoIceFloes && nextTimeIceFloesAllowed < DateTime.Now && Me.IsMoving && utils.CanCast(ICE_FLOES) && !utils.isAuraActive(ICE_FLOES_BUFF) /*&& utils.PlayerCountBuff(ARCANE_CHARGE) < 4*/)
                {
                    utils.LogActivity(ICE_FLOES);
                    utils.Cast(ICE_FLOES);
                    SetNextTimeIceFloesAllowed();
                }
                updateValue();

                if ((SoloBotType || ArcaneMageSettings.Instance.movementEnabled) && Me.IsMoving && target.InLineOfSpellSight && target.Distance - target.CombatReach - 1 <= ArcaneMageSettings.Instance.PullDistance)
                {
                    Navigator.PlayerMover.MoveStop();
                }
            }
            else if (ArcaneMageSettings.Instance.movementEnabled && Me.CurrentTarget != null && !Me.CurrentTarget.IsDead && (!Me.CurrentTarget.InLineOfSpellSight || Me.CurrentTarget.Distance - Me.CurrentTarget.CombatReach - 1 > ArcaneMageSettings.Instance.PullDistance))
            {
                movement.KingHealMove(Me.CurrentTarget, ArcaneMageSettings.Instance.PullDistance);
            }
            return false;
        }

        private bool UseCD()
        {
            if (Me.Combat && Me.GotTarget)
            {
                if (utils.CanCast(ARCANE_POWER) && utils.PlayerCountBuff(ARCANE_CHARGE) == 4 && ArcaneMageSettings.Instance.CDUseArcanePower == ArcaneMageSettings.CDUseType.COOLDOWN)
                {
                    utils.LogActivity(ARCANE_POWER);
                    return utils.Cast(ARCANE_POWER);
                }
                if (utils.CanCast(MIRROR_IMAGE) && utils.PlayerCountBuff(ARCANE_CHARGE) == 4 && ArcaneMageSettings.Instance.CDUseMirrorImage == ArcaneMageSettings.CDUseType.COOLDOWN)
                {
                    utils.LogActivity(MIRROR_IMAGE);
                    return utils.Cast(MIRROR_IMAGE);
                }
                

                if (extra.IsTargetBoss())
                {
                    if (utils.CanCast(ARCANE_POWER) && utils.PlayerCountBuff(ARCANE_CHARGE) == 4 && ArcaneMageSettings.Instance.CDUseArcanePower == ArcaneMageSettings.CDUseType.BOSS)
                    {
                        utils.LogActivity(ARCANE_POWER);
                        return utils.Cast(ARCANE_POWER);
                    }
                    if (utils.CanCast(MIRROR_IMAGE) && utils.PlayerCountBuff(ARCANE_CHARGE) == 4 && ArcaneMageSettings.Instance.CDUseMirrorImage == ArcaneMageSettings.CDUseType.BOSS)
                    {
                        utils.LogActivity(MIRROR_IMAGE);
                        return utils.Cast(MIRROR_IMAGE);
                    }
                    
                }
            }
            return false;
        }

        private bool Decursing()
        {
            if (ArcaneMageSettings.Instance.UseDecurse && utils.CanCast(REMOVE_CURSE))
            {
                WoWUnit player = null;
                if (utils.NeedsDeCurse(Me))
                    player = Me;
                else player = utils.GetDecurseTarget(40f);

                if (player != null && player.Distance - player.CombatReach -1  <= 40f && player.InLineOfSpellSight)
                {
                    utils.LogActivity(REMOVE_CURSE, player.Class.ToString());
                    return utils.Cast(REMOVE_CURSE, player);
                }
            }
            return false;
        }

        private bool goForEvocation = false;

        private bool RecMana()
        {
            if (goForEvocation || (ArcaneMageSettings.Instance.UseEvocationBurnPhase 
                && !Me.IsChanneling && !Me.IsMoving 
                && Me.ManaPercent <= ArcaneMageSettings.Instance.EvocationToRecMana
                && TimeEvocationAgainUp() == 0))
            {
                goForEvocation = true;
                if (missiles >= 1 && charges >= 4)
                {
                    utils.LogActivity("Near to cast evocation: missiles=" + missiles + " charges=" + charges + "   " + ARCANE_MISSILES, target.SafeName);
                    return utils.Cast(ARCANE_MISSILES, target);
                }
                updateValue();
                /*if (utils.CanCast(ARCANE_BARRAGE) && missiles==0)
                {
                    if (utils.CanCast(NETHER_TEMPEST) && charges > 0 && (utils.MyAuraTimeLeft(NETHER_TEMPEST, target) <= 3600)
                            && !(target.IsPlayer && ArcaneMageSettings.Instance.AvoidDOTPlayers))
                    {
                        utils.LogActivity(NETHER_TEMPEST, target.SafeName);
                        return utils.Cast(NETHER_TEMPEST, target);
                    }
                    utils.LogActivity("Near to cast evocation:  " + ARCANE_BARRAGE, target.SafeName);
                    return utils.Cast(ARCANE_BARRAGE, target);
                }*/
                if (utils.CanCast(NETHER_TEMPEST) && charges == 4 && (utils.MyAuraTimeLeft(NETHER_TEMPEST, target) <= 3600)
                            && !(target.IsPlayer && ArcaneMageSettings.Instance.AvoidDOTPlayers))
                {
                    utils.LogActivity(NETHER_TEMPEST, target.SafeName);
                    return utils.Cast(NETHER_TEMPEST, target);
                }
                if (TimeEvocationAgainUp() > ArcaneMageSettings.Instance.TimeSecondsEvocationAgainUp)
                    goForEvocation = false;
                else if (missiles == 0 || (missiles >= 1 && charges < 4))
                {
                    utils.LogActivity(EVOCATION);
                    return utils.Cast(EVOCATION);
                }
            }
            return false;
        }

        private bool PrismaticCrystal()
        {
            if (!ArcaneMageSettings.Instance.PrismaticCrystalOnlyOnBurst && talents.IsSelected(20) && !target.IsMoving && !Me.IsMoving && utils.CanCast(PRISMATIC_CRYSTAL)
                && (extra.IsTargetBoss() || utils.AllAttaccableEnemyMobsInRangeFromTarget(target, 8, ExtraUtilsSettings.Instance.provinGround_Targeting).Count() >= ArcaneMageSettings.Instance.PrismaticCrystalEnemiesNum))
            {
                if ( charges >= ArcaneMageSettings.Instance.PrismaticCrystalChargesRequired 
                    && missiles >= 1)
                {
                    utils.LogActivity(PRISMATIC_CRYSTAL, target.SafeName);
                    utils.Cast(PRISMATIC_CRYSTAL, target);
                    return SpellManager.ClickRemoteLocation(target.Location);
                }
            }
            return false;
        }
    }
}